![]() | |
|
|
|
To access the contents, click the chapter and section titles.
Bug Proofing Visual Basic: A Guide to Error Handling and Prevention
Indent the code inside a subroutine because it executes at a different level of scope than code outside the routine. Some programmers indent variable declarations within a routine because they are at the same level of scope as the code within the routine.
Private Sub MySub()
Dim name_counter As Integer
Dim student_number As Integer
For student_number = 1 To NumStudents
:
Next student_number
End Sub
I am accustomed to keeping variable declarations at the same level as the routines declaration. This separates the variable declarations more firmly from the code.
Private Sub MySub()
Dim name_counter As Integer
Dim student_number As Integer
For student_number = 1 To NumStudents
:
Next student_number
End Sub
Either method is fine as long as you are consistent. Remove or Assert AssumptionsEither remove or assert every assumption. For example, a subroutine that sorts the items in an array can use LBound and UBound to determine the arrays bounds. In that case, the routine may not need to make any assumptions about the arrays bounds. However, if the routine does make assumptions about the arrays bounds, it should use Debug.Assert or Stop statements to verify that the bounds meet those assumptions. The most straightforward implementation of the heapsort algorithm, for example, assumes the lower arrays lower bound is 1. A heapsort routine should use Debug.Assert or Stop to verify that assumption. Note that in either case, the routine assumes that the array has been allocated. LBound and UBound generate errors if the array has not yet been allocated. The following code shows how a program can protect itself if in this situation.
Private Sub SortArray(arr() As Integer)
Dim l_bound As Integer
' (Other declarations omitted)
:
' Verify that the array has been allocated.
On Error GoTo UnAllocated
l_bound = LBound(arr)
On Error GoTo 0 ' Resume normal error handling.
' Verify assumptions.
:
' Sort the array.
:
Exit Sub
UnAllocated:
If Err.Number = 9 Then
' The array has not been allocated.
Err.Raise err_UNALLOCATED_ARRAY, _
"MyProgram.MapValueToArray", _
"Parameter ""arr"" has not been allocated with ReDim"
Else
' Reraise the unknown error.
Err.Raise Err.Number, _
Err.Source, Err.Description, _
Err.HelpFile, Err.HelpContext
End If
End Sub
Catch Invalid SituationsWhen a routine encounters a situation that should never arise, it should let someone know about it. For example, a subroutine that expects a variant parameter to contain a filename should never receive a parameter that contains an array of integers. That situation never makes sense and there is no reasonable way the routine can figure out what to do. During the design phase, the routine can use Debug.Assert or Stop to make the program stop when it encounters an invalid situation. In a final program in which the debugging code has been removed, the routine should raise an error using the Err.Raise statement. While the calling routine probably cannot handle the error, it can try to continue running. In any case, the routine should always tell someone about the error. It should not quietly ignore the error and continue. Raise Errors for ExceptionsIf a routine encounters an exceptional condition, it should raise an error. This forces the calling routine to take exceptional action using an error handler. If a routine that saves data into a file cannot open the file, that is an important and exceptional condition. It should force the calling routine to take action by raising an error. During normal situations, if a routine must return information to the calling routine, it should return the information through a parameter passed ByRef or through its return value if it is a function. A routine should not raise an error under normal circumstances. Consider a routine that searches a phone directory for a specific name. Depending on the users input, this routine may fail to find the specified person. Failing to find a person is a normal situation so the routine should not raise an error. It should return a status code indicating whether it succeeded or failed. The calling routine must check the status code to see whether it found the person it wanted. Note that Microsofts Common Dialog Control violates this guideline. If a program sets the controls CancelError property to True, the control raises an error when the user cancels a file selection operation. It would be better if the control provided a Canceled property that indicated whether the user canceled the operation. Use Enums for Status CodesUse enumerated types or constants to define status codes. Do not use True, False, or magic numbers. The following statement uses a function named OpenDataFile. It is not clear from the code whether OpenDataFile returns True when it is successful or when it fails. If OpenDataFile(file_name) Then ... In the following version it is obvious that the If statement executes its code when the function fails.
Enum ofStatus
ofOpenFailed
ofOpenOk
End Enum
:
If OpenDataFile(file_name) = ofOpenFailed Then ...
Using Enums for status values not only makes the code more obvious, it also makes creating new status codes easier. Suppose a function returns True to indicate success and False to indicate failure. Now suppose you later discover that the function can fail in more than one way and the calling routine needs to know how it failed. Making the function accommodate this change is difficult. If the function returns enumerated status codes instead of True and False, adding a new status code is easy. Return the Worst Data Possible for ErrorsWhen a routine encounters an error and returns an error status code, it should return the worst possible value. This encourages the calling routine to check the status code before it uses the return values. For example, suppose a routine searches an array for a books title and returns the books index in the array. If it cannot find a book, the routine should return an error status code. For instance, it could set its return value to 32,767. If the programmer who wrote the calling function does not check the status code and tries to use this return value, the program will probably crash and the error will be obvious. On the other hand, if the search routine returns the value 0, the calling routine might be able to continue, depending on the lower bound of the array. This bug would be much harder to find. Note that Visual Basic default values look quite sensible. For example, the default value for an uninitialized integer is 0. If a routine does not take specific action to return unreasonable values like 32,767, the result may look useable to the calling routine.
|
|
Products | Contact Us | About Us | Privacy | Ad Info | Home
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-1999 EarthWeb Inc. All rights reserved. Reproduction whole or in part in any form or medium without express written permision of EarthWeb is prohibited.
|